#include <stdio.h>
#include <stdlib.h>

// TODO check internal functions considering the atoms admit tables now

static unsigned interrupted = 0;

#define ALLOC(s) alloc_mem(s)
#define FREE(s) free_mem(s)

typedef enum {
   NUM,
   SYMB,
   LIST,
   TABLE,
   LAMBDA
} Type;

typedef struct List {
   struct Atom *head;
   struct List *tail;
} List;

typedef struct Table {
   int *key;
   struct Atom *value;
   struct Table *next;
} Table;

typedef struct Atom {
   Type type;
   union {
      int num;
      int *symb;
      List *list;
      Table *table;
   };
} Atom;

typedef struct builtin_entry {
   int name[15];
   Atom *(*operation)(List*,Table**);
} builtin_entry;

// error and warning messages:
#define EMPTY_STACK ": Empty stack"
#define INSUFFICIENT_ARGS ": Insufficient arguments"
#define INSUFFICIENT_ARGS_STK ": Insufficient arguments (stack)"
#define INVALID_ARGS ": Invalid arguments"
#define INVALID_ARGS_STK ": Invalid arguments (stack)"
#define TOO_SHORT ": Too short"

#define MUST_BE_INT ": Must be a integer"
#define MUST_BE_SYM ": Must be a symbol"
#define MUST_BE_LIST ": Must be a list"
#define MUST_BE_SYMLIST ": Must be a symbol list"
#define MUST_BE_TABLE ": Must be a table"
#define MUST_BE_LAMBDA ": Must be a lambda"

#define DUPLICATE_NULL ": Tried to duplicate a null"
#define FREE_NULL ": Tried to free a null"
#define EXTRA_PARENTHESIS ": Extra closing parenthesis"


// basic input and output:
int _getchar();
Atom* _warning(char*);
Atom* _error(char*);

// printing:
void print_symbol(int*);
void print_expression(Atom*);

// duplicating and freeing:
Atom* duplicate_atom(Atom*);
List* duplicate_list(List*);
Table* duplicate_table(Table*);
int* duplicate_symbol(int*);
void free_atom(Atom*);
void free_list(List*);
void free_table(Table*);

// creating objects:
List* cons(Atom*, List*);
Atom* create_atom(Type);
Atom* create_int(int);
Atom* create_symbol(int*);
Atom* create_list(List*);
Atom* create_lambda(List*);

// stack manipulation:
void stack_push(Atom*, List**);
void stack_pop(int, List**);

// type operations:
int is_simple(Atom*);
int is_list(Atom*);
int is_symbol(Atom*);
int valid_lambda(List*);
unsigned is_symbol_list(List*);

// parsing:
int skip_whitespace();
Atom* parse_list();
Atom* parse_symbol(int);
Atom* parse_int(int);
Atom* parse();

// table operations:
unsigned symbols_are_equal(int*, int*);
Table* table_lookup(Table*, int*);
Table* table_set(Table**, int*, Atom*);
void table_union(Table*, Table**);
unsigned table_unset(Table**, int*);
int table_size(Table*);

// object comparation:
unsigned equal(Atom*, Atom*);
unsigned lists_are_equal(List*, List*);
int list_size(List*);

// expression evaluation:
void* (builtin_lookup)(int*);
Table* atom_lookup(int* name, Table* context);
Atom* eval(Atom*, Table**);
Atom* eval_symbol(int*, List*, Table**);
Atom* eval_application(List*, Table**);
Atom* eval_lambda(List*, List*, Table**);
unsigned define_local_vars (List*, List*, Table**);
void destroy_local_vars (List*, Table**);

#define DECL_BUILTIN(x) \
   Atom* x (List*, Table**)
#define BUILTIN(x) \
   Atom* x (List* li, Table** ctx)

// TODO review language semantics as a whole
// TODO add table instructions and adapt list instructions to include table semantics

// builtin functions:
// stack focused:
DECL_BUILTIN(_dup);     // dupplicates the stack top element
DECL_BUILTIN(_drop);    // excludes the stack top element
DECL_BUILTIN(_clear);   // clear the entire stack
DECL_BUILTIN(_swap);    // swap the 2 most recent stack elements
DECL_BUILTIN(_over);    // copies the 2nd most recent stack element to the top
DECL_BUILTIN(_depth);   // returns the number of elements at the stack
DECL_BUILTIN(_pick);    // copies an arbitrary stack element to the top, given its stack position
// list/table focused:
// NOTE symbols are casted to a list containing its characters as numbers
// NOTE numbers are casted to a list by the list-creating functions
// NOTE list/table indexing functions don't accept simple atoms as their first arguments
DECL_BUILTIN(_head);    // returns the first list/table element
                        // (head (list 1 2 3)) -> 1
                        // (head (table a 1 b 2 c 3)) -> 3
DECL_BUILTIN(_tip);     // returns the last list/table element
                        // (tip (list 1 2 3)) -> 3
                        // (tip (table a 1 b 2 c 3)) -> 3
DECL_BUILTIN(_tail);    // returns all but the first list/table element
                        // (tail (list 1 2 3)) -> (list 2 3)
                        // (tail (table a 1 b 2 c 3)) -> (table b 2 c 3)
DECL_BUILTIN(_first);   // returns a list/table containing the first list/table elements in the requested quantity
                        // (first (list 1 2 3) 2) -> (list 1 2)
                        // (first (table a 1 b 2 c 3) 2) -> (table a 1 b 2)
DECL_BUILTIN(_last);    // returns a list/table containing the last list/table elements in the requested quantity
                        // (first (list 1 2 3) 2) -> (list 1 2)
                        // (first (table a 1 b 2 c 3) 2) -> (table a 1 b 2)
DECL_BUILTIN(_get);     // gets a list/table element given its index (indexes counting from 1)
                        // (get (list 1 2 3) 2) -> 2
                        // (get (table a 1 b 2 c 3) b) -> 2
DECL_BUILTIN(_put);     // puts new elements at arbitrary positions in the list given its indexes
                        // or adds new key/value pairs in a table
                        // (put (list 1 2 3) 1 9 1 8) -> (list 8 9 1 2 3)
                        // (put (table a 1 b 2 c 3) d 8 e 9) -> (table a 1 b 2 c 3 d 8 e 9)
DECL_BUILTIN(_find);    // finds elements positions/indexes in a list/table
                        // (find (list 9 8 7 6 5) 4 8 7) -> (list 0 2 3)
                        // (find (table a 9 b 8 c 7) 4 8 7) -> (list 0 b c)
DECL_BUILTIN(_match);   // matches sequences of sequentially elements at a list, returning its initial positions
                        // (match (list 1 2 3 9 8 7) (list 9 8 7) (list 2 3 9)) -> (list 4 2)
DECL_BUILTIN(_slice);   // returns slices of a list, always requiring 2 indexes for each slice
                        // (slice (list 1 2 3 9 8 7) 2 3 5 6) -> (list 2 3 8 7)
DECL_BUILTIN(_concat);  // concatenates lists
                        // (concat (list 1 2 3) (list 9 8 7)) -> (list 1 2 3 9 8 7)
DECL_BUILTIN(_append);  // appends elements at the end of a list
                        // (append (list 1) 2 3) -> (list 1 2 3)
DECL_BUILTIN(_prepend); // appends elements at the start of a list
                        // (prepend (list 1) 2 3) -> (list 2 3 1)
// functional utilities:
DECL_BUILTIN(_map);     //
DECL_BUILTIN(_apply);   //
DECL_BUILTIN(_reduce);  //
DECL_BUILTIN(_filter);  //
DECL_BUILTIN(_quote);   //
DECL_BUILTIN(_unquote); //
// symbol utilities:
DECL_BUILTIN(_hash);
DECL_BUILTIN(_concat);
DECL_BUILTIN(_split);
// compound objects creation, or type coercion:
DECL_BUILTIN(_list);    // coerces a lambda or table to a list, creates a new list
DECL_BUILTIN(_lambda);  // coerces a list to a lambda, creates a new lambda
DECL_BUILTIN(_table);   // coerces a list to a table, creates a new table
// basic numeric operations:
DECL_BUILTIN(_sum);     // "+"
DECL_BUILTIN(_sub);     // "-"
DECL_BUILTIN(_mul);     // "*"
DECL_BUILTIN(_div);     // "/"
DECL_BUILTIN(_rem);     // "%"
DECL_BUILTIN(_power);   // "^"
// numeric comparisons:
DECL_BUILTIN(_eq);      // "="
DECL_BUILTIN(_lt);      // "<"
DECL_BUILTIN(_le);      // "<="
DECL_BUILTIN(_gt);      // ">"
DECL_BUILTIN(_ge);      // ">="
// logic utilities:
DECL_BUILTIN(_and);
DECL_BUILTIN(_or);
DECL_BUILTIN(_xor);
DECL_BUILTIN(_not);
// flow control:
DECL_BUILTIN(_if);
DECL_BUILTIN(_when);
DECL_BUILTIN(_case);
DECL_BUILTIN(_conds);
DECL_BUILTIN(_for);
DECL_BUILTIN(_while);
DECL_BUILTIN(_recur);
DECL_BUILTIN(_do);
// generics:
DECL_BUILTIN(_eval);
DECL_BUILTIN(_print);
DECL_BUILTIN(_equal);
DECL_BUILTIN(_stack);
DECL_BUILTIN(_help);
DECL_BUILTIN(_catalog);
// operational:
DECL_BUILTIN(_user_error);
DECL_BUILTIN(_user_warning);
DECL_BUILTIN(_option);
DECL_BUILTIN(_quit);
DECL_BUILTIN(_trap);
